/**
 * AmiCoPyJava
 * @file	PythonCallsJava.cc
 * @brief	PythonCallsJava main file
 *
 * @author Sergey.Karakovskiy at idsia.ch, Nikolay.Sohryakov at gmail.com
 *
 * Keep in mind, that ID of the @c Java method is a pointer to the @c _jmethodID @c Java class
 * or just simply value of type @c jmethodID.
 */ 

#include <Python.h>

#ifdef __APPLE__
 #include <sys/stat.h>
 #include <sys/resource.h>
 #include <pthread.h>
 //#undef __CONDITIONALMACROS__
 #include <CoreFoundation/CoreFoundation.h>
#else
 #include <jni.h>
#endif
#include <iostream>
#include "PythonCallsJava.h"
#include "arrayutils.h"
#include <vector>
#include <map>
#include <string>
#include <stdint.h>
#include <stdarg.h>
#include <queue>
#include <typeinfo>
#include <string.h>
#include <algorithm>

/**
 @brief A struct to store parsed signature.
 
 Signature of the method looks like @e "(JLjava/lang/String;[I)[S". @n
 First param has type @c long, second is @c String and third is an array of @c int.
 When parsed @a args will contain following symbols: ('J', 'L', '[', 'I'), @a ret will contain ('[', 'S') and
 @a arrays will be equal to 1 cause of there is only one array passed in to the method.
*/
struct mthdSign
{
	/*@{*/
    std::queue<char> args; /**< Parsed arguments */
    std::queue<char> ret; /**< Parsed return value */
    int arrays;  /**< Number of arrays in the signature */
    /*@}*/

};

/**
 Version of the @c Java @c Virtual @c Machine.
 */
const int JVM_VERSION = 0x00010004;

static JNIEnv *env = NULL; /**< Pointer to the @c JNIEnv */
static JavaVM *jvm; /**< Pointer to the @c JVM */

static jclass jClass; /**< Pointer to the loaded @c Java class */
static jobject obj; /**< Main object */

///Name of the method is a string consists from three parts: name of the method, @c funcSeparator and signature.
static std::map<std::string, jmethodID> usedIDs; /**< Associates name of the method and it's ID. */
static std::map<jmethodID, std::string> jmIDSignature; /**< Associates ID of the method and it's signature. */

/*
 * Custom code. Mario AI Specific variables and methods:
 */
static jmethodID midReset;
static jmethodID midIsLevelFinished;
static jmethodID midTick;
static jmethodID midGetEvaluationInfo;
//static jmethodID midGetEntireObservation;
static jmethodID midGetSerializedLevelSceneObservationZ;
static jmethodID midGetSerializedEnemiesObservationZ;
static jmethodID midGetMarioFloatPos;
static jmethodID midGetEnemiesFloatPos;
static jmethodID midGetMarioState;
static jmethodID midPerformAction;
static jmethodID midGetReceptiveFieldWidth;
static jmethodID midGetReceptiveFieldHeight;
static jmethodID midGetMarioReceptiveFieldCenter;

void initMarioAIBenchmark();
/*
 *
 */


/**
 Function @c setArraySize must be invoked before calling of the method that takes arrays as arguments.
 
 @brief Dynamic array keeping number of elements in corresponding array in method args. 
*/
unsigned int* arraysSize;

const char funcSeparator = '#'; /**< Separator between function name and signature */

void destroyVM();

/** 
 @brief Takes a jmethodID of the method by it's name and signature and validates it.

 If not valid, then destroy @c JVM.
 
 @param methodName name of the method
 @param methodSig signature of the method

 @return ID of the method - @c jmethodID
 */
jmethodID getMethodIDSafe(const char* methodName, const char* methodSig)
{
    jmethodID mid = (env)->GetMethodID(jClass, methodName, methodSig);
    if (mid == NULL) 
	{
        std::cerr << "AmiCo: Can't find method " << methodName << " with signature " << methodSig << std::endl;
        std::cerr.flush();
        destroyVM();
    } else
    {
        std::cout << "AmiCo: " << methodName << " found!! " << mid << std::endl;
        std::cout.flush();
    }

    return mid;
}

/** 
 @brief Intializes @c amico library.
 
 Creates a @c JVM with given startup options. If they were errors they will be printed to the standard error output.
 
 @param nOptions Number of startup options.
 @param ... variable list of @c JVM startup options given as strings(i.e. each parameter is a separate startup option).
 */
_AMICOPYJAVA_API void amicoInitialize(int nOptions, ...)
{
    if (env != NULL)
    {
        std::cerr << "\nAmiCo: WARNING: Attempt to recreate JVM environment rejected. Using existing one\n";
        std::cerr.flush();
        return;
    }
    jint res;

    JavaVMInitArgs vm_args;
    vm_args.nOptions = nOptions;
    JavaVMOption* options = new JavaVMOption[nOptions];
    
    if (nOptions > 0)
    {
        va_list vl;
        va_start(vl, nOptions);
        for (int i = 0; i < nOptions; i++)
        {
            options[i].optionString = va_arg(vl, char*);
        }
        va_end(vl);

        if (nOptions > 0)
            vm_args.options = options;
    }
    
    vm_args.version = JVM_VERSION;
    vm_args.ignoreUnrecognized = JNI_FALSE; // do not ignore unrecognized JVM startup options
    /* Create the Java VM */

    std::cout << "AmiCo: Trying to create a JVM ..." << std::endl; std::cout.flush();
    res = JNI_CreateJavaVM(&jvm, (void**) &env, &vm_args);

    if (res < 0)
    {
        std::cerr << "\nAmiCo: Can't create Java VM\n";       
        std::cerr.flush();
        return;
    }

    std::cout << "AmiCo: Initialization finished. JVM created succesfully" << std::endl; std::cout.flush();
}

/**
 @brief Loads a @c Java class in to the memory.

 If they were errors they will be printed to the standard error output.
 
 @param javaClassName Path to the @c Java class to be loaded without suffix @c .class.
 */
_AMICOPYJAVA_API void loadJavaClass(const char* javaClassName)
{
    std::cout << "AmiCo: Search for class: " << javaClassName;
    jClass = (env)->FindClass(javaClassName);
    if (jClass == NULL)
    {
        std::cerr << std::endl << "AmiCo: Can't find class " << javaClassName << std::endl;
        std::cerr.flush();
        destroyVM();
    }

    std::cout << "\nAmiCo: Creating new object:\n";
    std::cout << "\nAmiCo: Class =" << jClass << "   env = " << env << std::endl;
    std::cout.flush();

    obj = (env)->NewObject(jClass, env->GetMethodID(jClass, "<init>", "()V"));
    if (obj == NULL)
    {
        std::cerr << std::endl << "AmiCo: Can't Create Object " << javaClassName << std::endl;
        std::cerr.flush();
        destroyVM();
    } else
        std::cout << std::endl << "AmiCo: Object created succesfully: " << obj << std::endl;
}

/**
 @brief Gets an ID of the method  and returns pointer to it. Pointer in @c Python is stored like a number.
 
 Checks if ID of the given method is already in the @c usedIDs maps than returns
 ID from the map, else invokes @c getMethodIDSafe, puts ID in the map and returns it.
 In @c Python this value is stored as @c ctypes.c_void_p.
 If they were errors they will be printed to the standard error output.
 
 @param mName Name of the method in the class.
 @param mSign Signature of the method in the class. See @c JNI documentation for information about composing it.

 @return Pointer to the method ID.
 */
_AMICOPYJAVA_API void* getMethod(const char* mName, const char* mSign)
{
    //method name + sepatator + signature.
    //for example: foo#(II)I
    std::string mthd = std::string(mName) + funcSeparator + std::string(mSign);
    jmethodID mid = usedIDs[mthd];

    if (mid != NULL)
    {
        return mid;
    }

    mid = getMethodIDSafe(mName, mSign);
    usedIDs[mthd] = mid;
    
    jmIDSignature[mid] = std::string(mSign);

    return mid;
}

/**
 @brief Parses signature.

 Parses signature in to the @c mthdSign type.
 To identify type @c String used symbol @e L without suffix(path to the class).
 
 @param signature Signature of the method.

 @return Parsed signature stored in the @c mthdSign.
 */
mthdSign parseSignature(std::string signature)
{
    mthdSign res;
    res.arrays = 0;

    std::string::iterator it;
    for (it = signature.begin() + 1; it != signature.end(); it++)
    {
        if (*it == ')')
            break;

        if (*it == '[')
            res.arrays++;

        res.args.push(*it);
        //std::cout << *it << std::endl;

        if (*it == 'L')
        {
            it += 17;
        }
    }

    if (it == signature.end())
    {
        //ERROR. This should not happen!
        //If it happens, return value will be empty and should be hanled later
        return res;
    }

    //here we parse result type
    //queue is useed as result type can be an array
    ++it;
    for (it; it != signature.end(); it++)
    {
        res.ret.push(*it);

        if (*it == 'L')
        {
            break;
        }
    }

    return res;
}

/**
 @brief Validates ID of the method
 
 Parses signature in to the @c mthdSign type.
 To identify type @c String used symbol @e L without suffix(path to the class).
 
 @param midUInt ID of the method

 @return True if ID is valid, false otherwise.
 */
inline bool check_midUInt(void* midUInt)
{
    if (midUInt == NULL)
    {
        std::cerr << "AmiCo: midUInt is NULL!" << std::endl;
        std::cerr.flush();
        return false;
    }
    return true;
}

/**
 @brief Generalizes call of the @c Call<Type>MethodA @c JNI function.
 
 Calls @c Call\<Type\>MethodA depend on type given as char(from element in the queue @a type).
 
 @param midUInt ID of the method.
 @param type Parsed argument part of the signature of the method
 @param args Array of arguments to be passed in to the @c Java method.

 @return Result of performing of the Call\<Type\>MethodA.
 */
template<typename T>
T callMethodByType (void* midUInt, std::queue<char>& type, jvalue* args)
{
    if (!check_midUInt(midUInt))
        return (T) NULL;

    jmethodID mid = (jmethodID) midUInt;
    switch (type.front())
    {
        case 'Z': //boolean
            return (T) (env)->CallBooleanMethodA(obj, mid, args);
        case 'B': //byte
            return (T) (env)->CallByteMethodA(obj, mid, args);
        case 'C': //char
            return (T) (env)->CallCharMethodA(obj, mid, args);
        case 'S': //short
            return (T) (env)->CallShortMethodA(obj, mid, args);
        case 'I': //integer
            return (T) (env)->CallIntMethodA(obj, mid, args);
        case 'J': //long
            return (T) (env)->CallLongMethodA(obj, mid, args);
        case 'F': //float
            return (T) (env)->CallFloatMethodA(obj, mid, args);
        case 'D': //double
            return (T) (env)->CallDoubleMethodA(obj, mid, args);
        case 'L': //string
            break; /// Instantiated for @c type.front() = @c 'L' (result type is a string)
        case 'V': //void
            (env)->CallVoidMethodA(obj, mid, args);
        case '[': //array of <next element in queue>
        {
            break; ///TODO: fix it.
        }
    }
}

/**
 @brief Instantiated from callMethodByType for @a T = @c jobject
 
 @param midUInt ID of the method.
 @param type Parsed argument part of the signature of the method
 @param args Array of arguments to be passed in to the @c Java method.

 @return Result of performing of the CallObjectMethodA.
 */
template<>
jobject callMethodByType<jobject> (void* midUInt, std::queue<char>& type, jvalue* args)
{
    if (!check_midUInt(midUInt))
        return NULL;

    jmethodID mid = (jmethodID) midUInt;

    if(args == NULL)
    {
        std::cout << "AmiCo: args is null\n"; std::cout.flush();
    }

    jobject o = (env)->CallObjectMethodA(obj, mid, args);

    if (o == NULL)
        std::cout << "AmiCo: callMethodByType for jobject is null\n"; std::cout.flush();
    return o;
}

/**
 @brief Instantiated from callMethodByType for @a T = @c jobject (result type is string)
 
 @param midUInt ID of the method.
 @param type Parsed argument part of the signature of the method
 @param args Array of arguments to be passed in to the @c Java method.

 @return Result of performing of the CallObjectMethodA.
 */
template<> std::string callMethodByType<std::string>(void* midUInt, std::queue<char>& type, jvalue* args)
{
    if (!check_midUInt(midUInt))
        return "";

    jmethodID mid = (jmethodID) midUInt;
    const char* res;
    jstring js = (jstring) (env)->CallObjectMethodA(obj, mid, args);

    res = (env)->GetStringUTFChars(js, NULL);
    std::string s = std::string(res);
    if (res == NULL)
    {
        return s; /* OutOfMemoryError already thrown */
    }
    (env)->ReleaseStringUTFChars(js, res);
    return s;
}

/**
 @brief Generalizes call of the @c convertPythonArrayToJavaArray function.
 
 This function is needed to convert a native @c C++ array in to the @c Java array. Depend on the @a type of the
 array @c convertPythonArrayToJavaArray invoked for this type of the array.
 
 @param type Type of the elementns of the array.
 @param sizeArrayPos Position in the @c arraysSize array.
 @param vl Pointer to the array in the variable list.

 @return @c Java array as @c jobject.
 */
jobject getJArray(char type, int sizeArrayPos, va_list vl)
{
    unsigned int arSize = arraysSize[sizeArrayPos];

    switch (type) {
        case 'Z': //boolean
        {
            jbooleanArray res_array;
            unsigned char* buf = new unsigned char[arSize];
			unsigned char* tmp = va_arg(vl, unsigned char*);
			std::copy(tmp, tmp + arSize, buf);

            res_array = convertPythonArrayToJavaArray<unsigned char, jbooleanArray, jboolean > (env, buf, type, arSize);
            
            delete[] buf;            
            return res_array;
        }
        case 'B': //byte
        {
            jbyteArray res_array;
            signed char* buf = new signed char[arSize];
			signed char* tmp = va_arg(vl, signed char*);
			std::copy(tmp, tmp + arSize, buf);

            res_array = convertPythonArrayToJavaArray<signed char, jbyteArray, jbyte > (env, buf, type, arSize);

            delete[] buf;
            return res_array;
        }
        case 'C': //char
        {
            jcharArray res_array;
            unsigned short* buf = new unsigned short[arSize];
			unsigned short* tmp = va_arg(vl, unsigned short*);
			std::copy(tmp, tmp + arSize, buf);

            res_array = convertPythonArrayToJavaArray<unsigned short, jcharArray, jchar > (env, buf, type, arSize);

            delete[] buf;
            return res_array;
        }
        case 'S': //short
        {
            jshortArray res_array;
            short* buf = new short[arSize];
            short* tmp = va_arg(vl, short*);
			std::copy(tmp, tmp + arSize, buf);	

            res_array = convertPythonArrayToJavaArray<short, jshortArray, jshort > (env, buf, type, arSize);

            delete[] buf;
            return res_array;
        }
        case 'I': //integer
        {
            jintArray res_array;
            int* buf = new int[arSize];
            int* tmp = va_arg(vl, int*);
			std::copy(tmp, tmp + arSize, buf);
	    
            res_array = convertPythonArrayToJavaArray<int, jintArray, jint > (env, buf, type, arSize);

            delete[] buf;
            return res_array;
        }
        case 'J': //long
        {
            jlongArray res_array;
            long* buf = new long[arSize];
            long* tmp = va_arg(vl, long*);
			std::copy(tmp, tmp + arSize, buf);

            res_array = convertPythonArrayToJavaArray<long, jlongArray, jlong > (env, buf, type, arSize);

            delete[] buf;
            return res_array;
        }
        case 'F': //float
        {
            jfloatArray res_array;
            float* buf = new float[arSize];
            float* tmp = va_arg(vl, float*);
			std::copy(tmp, tmp + arSize, buf);

            res_array = convertPythonArrayToJavaArray<float, jfloatArray, jfloat > (env, buf, type, arSize);

            delete[] buf;
            return res_array;
        }
        case 'D': //double
        {
            jdoubleArray res_array;
            double* buf = new double[arSize];
            double* tmp = va_arg(vl, double*);
			std::copy(tmp, tmp + arSize, buf);

            res_array = convertPythonArrayToJavaArray<double, jdoubleArray, jdouble > (env, buf, type, arSize);

            delete[] buf;
            return res_array;
        }
        case 'L': //string
        {
            jobjectArray res_array = convertPythonArrayToJavaArray<char*, jobjectArray, jobject>(env, va_arg(vl, char**), 'L', arSize);
            return res_array;
        }
    }
    return NULL;
}

/**
 @brief Builds arguments array and calls @c callMethodByType

 Creates an array of @c jvalue and converts elements from variable list in to @c JNI types.
 Type is determined by it's symbol name in the signature in turn.
 
 @param midUInt ID of the method.
 @param signature Signature of the method given as string.
 @param vl Pointer to the variable list with arguments to be passed in to the @c Java method.

 @return Result of performing @c Java method.
 */
template<typename T>
T callTMethod (void* midUInt, std::string signature, va_list vl)
{
    if (!check_midUInt(midUInt))
        return (T) NULL;

    jmethodID mid = (jmethodID) midUInt;
    mthdSign sign = parseSignature(signature);
    jvalue* args = new jvalue[sign.args.size() - sign.arrays]; //arguments to pass in to java method

    int s = sign.args.size() - sign.arrays;
    int resArrayPos = 0;
    // <editor-fold defaultstate="collapsed" desc="put va_arg in to jvalue* array">
    int sizeArrayPos = 0;
    while (!sign.args.empty())
    {
        char type = sign.args.front();

        switch (type)
        {
            case 'Z': //boolean
            {
                args[resArrayPos].z = (jboolean) va_arg(vl, int);
                break;
            }
            case 'B': //byte
            {
                args[resArrayPos].b = (jbyte) va_arg(vl, int);
                break;
            }
            case 'C': //char   should be passed here by it's code
            {
                args[resArrayPos].c = (jchar) va_arg(vl, int);
                break;
            }
            case 'S': //short
            {
                args[resArrayPos].s = (jshort) va_arg(vl, int);
                break;
            }
            case 'I': //integer
            {
                args[resArrayPos].i = (jint) va_arg(vl, int);
                break;
            }
            case 'J': //long
            {
                //if to pass value smaller than -2147483648 exception raised.
                //jlong is long long.
                long long qq = va_arg(vl, long long);
                args[resArrayPos].j = (jlong) qq;
                break;
            }
            case 'F': //float
            {
                float temp = va_arg(vl, double);
                args[resArrayPos].f = (jfloat) temp;
                break;
            }
            case 'D': //double
            {
                args[resArrayPos].d = (jdouble) va_arg(vl, double);
                break;
            }
            case 'L': //string
            {
                char* str = va_arg(vl, char*);
                args[resArrayPos].l = (env)->NewStringUTF(str);
                break;
            }
            case '[': //array of <next element in queue>
            {
                sign.args.pop();
                char ar_type = sign.args.front(); //type of the array

                args[resArrayPos].l = getJArray(ar_type, sizeArrayPos, vl);
                va_arg(vl, void*);

                ++sizeArrayPos;
                break;
            }
        }
        sign.args.pop();

        ++resArrayPos;
        std::cout.flush(); 
    }// </editor-fold>

    //return CallMethodByName(...)
    return callMethodByType<T>(midUInt, sign.ret, args);
}

/**
 @brief Takes signature of the method from @a jmIDSignature map by it's ID.
 
 @param midUInt ID of the method.
 
 @return Signature of the method as string.
 */
std::string getMethodSignature(void* midUInt)
{
    if (!check_midUInt(midUInt))
        return "";

    jmethodID mid = (jmethodID) midUInt;
    std::string res = jmIDSignature[mid];
    if (res == "")
    {
        std::cerr << "AmiCo: Signature of the method with ID " << mid << " not found"; std::cerr.flush();
        return "";
    }

    return res;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Programmers place all arguments that are to be passed to the method immediately
 following the @c midUInt argument. The @c callVoidMethod function
 accepts these arguments, converts and passes them to the method that the programmer
 would like to be invoked.
 If they were errors they will be printed to the standard error output.
 
 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.
 */
_AMICOPYJAVA_API void callVoidMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return;

    std::string signature = getMethodSignature(midUInt);
    if (signature.empty())
        return;

    va_list vl;
    va_start(vl, midUInt);
    callTMethod<void>(midUInt, signature, vl);
    va_end(vl);
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Programmers place all arguments that are to be passed to the method immediately
 following the @c midUInt argument. The @c callIntMethod function
 accepts these arguments, converts and passes them to the method that the programmer
 would like to be invoked.
 If they were errors they will be printed to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.
 
 @return @c The result of calling the method. Value of the @c integer type.
 */
_AMICOPYJAVA_API int callIntMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return 0;

    std::string signature = getMethodSignature(midUInt);
    if (signature.empty())
        return 0;

    va_list vl;
    va_start(vl, midUInt);
    int res = callTMethod<int>(midUInt, signature, vl);
    va_end(vl);
    
    return res;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Programmers place all arguments that are to be passed to the method immediately
 following the @c midUInt argument. The @c callShortMethod function
 accepts these arguments, converts and passes them to the method that the programmer
 would like to be invoked.
 If they were errors they will be printed to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.

 @return @c The result of calling the method. Value of the @c short type.
 */
_AMICOPYJAVA_API short callShortMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return 0;
    
    std::string signature = getMethodSignature(midUInt);
    if (signature.empty())
        return 0;

    va_list vl;
    va_start(vl, midUInt);
    short res = callTMethod<short>(midUInt, signature, vl);
    va_end(vl);

    return res;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Programmers place all arguments that are to be passed to the method immediately
 following the @c midUInt argument. The @c callLongMethod function
 accepts these arguments, converts and passes them to the method that the programmer
 would like to be invoked.
 If they were errors they will be printed to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.

 @return @c The result of calling the method. Value of the @c long type.
 */
_AMICOPYJAVA_API long callLongMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return 0;

    std::string signature = getMethodSignature(midUInt);
    if (signature.empty())
        return 0;

    va_list vl;
    va_start(vl, midUInt);
    long res = callTMethod<long>(midUInt, signature, vl);
    va_end(vl);

    return res;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Programmers place all arguments that are to be passed to the method immediately
 following the @c midUInt argument. The @c callCharMethod function
 accepts these arguments, converts and passes them to the method that the programmer
 would like to be invoked.
 If they were errors they will be printed to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.

 @return @c The result of calling the method. Value of the @c unsigned @c short type.
 */
_AMICOPYJAVA_API unsigned short callCharMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return 0;

    std::string signature = getMethodSignature(midUInt);
    if (signature.empty())
        return 0;

    va_list vl;
    va_start(vl, midUInt);
    unsigned short res = callTMethod<unsigned short>(midUInt, signature, vl);
    va_end(vl);

    return res;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Programmers place all arguments that are to be passed to the method immediately
 following the @c midUInt argument. The @c callByteMethod function
 accepts these arguments, converts and passes them to the method that the programmer
 would like to be invoked.
 If they were errors they will be printed to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.

 @return @c The result of calling the method. Value of the @c signed @c char type.
 */
_AMICOPYJAVA_API signed char callByteMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return 0;

    std::string signature = getMethodSignature(midUInt);
    if (signature.empty())
        return 0;

    va_list vl;
    va_start(vl, midUInt);
    signed char res = callTMethod<signed char>(midUInt, signature, vl);
    va_end(vl);

    return res;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Programmers place all arguments that are to be passed to the method immediately
 following the @c midUInt argument. The @c callBooleanMethod function
 accepts these arguments, converts and passes them to the method that the programmer
 would like to be invoked.
 If they were errors they will be printed to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.

 @return @c The result of calling the method. Value of the @c unsigned @c char type.
 */
_AMICOPYJAVA_API unsigned char callBooleanMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return 0;

    std::string signature = getMethodSignature(midUInt);
    if (signature.empty())
        return 0;

    va_list vl;
    va_start(vl, midUInt);
    unsigned char res = callTMethod<unsigned char>(midUInt, signature, vl);
    va_end(vl);

    return res;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Programmers place all arguments that are to be passed to the method immediately
 following the @c midUInt argument. The @c callStringMethod function
 accepts these arguments, converts and passes them to the method that the programmer
 would like to be invoked.
 If they were errors they will be printed to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.

 @return @c The result of calling the method. Value of the @c char* type.
 */
_AMICOPYJAVA_API char* callStringMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return NULL;

    std::string signature = getMethodSignature(midUInt);
    if (signature.empty())
        return NULL;

    va_list vl;
    va_start(vl, midUInt);
    std::string res = callTMethod<std::string>(midUInt, signature, vl);
    va_end(vl);

    return const_cast<char*>(res.c_str());
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Programmers place all arguments that are to be passed to the method immediately
 following the @c midUInt argument. The @c callFloatMethod function
 accepts these arguments, converts and passes them to the method that the programmer
 would like to be invoked.
 If they were errors they will be printed to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.

 @return @c The result of calling the method. Value of the @c float type.
 
 @todo fix explanation about how to pass float types parameters from @c Python
 
 @attention pass options here like c_double
 */
_AMICOPYJAVA_API float callFloatMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return 0;

    std::string signature = getMethodSignature(midUInt);
    if (signature.empty())
        return 0;

    va_list vl;
    va_start(vl, midUInt);
    float res = callTMethod<float>(midUInt, signature, vl);
    va_end(vl);

    return res;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Programmers place all arguments that are to be passed to the method immediately
 following the @c midUInt argument. The @c callDoubleMethod function
 accepts these arguments, converts and passes them to the method that the programmer
 would like to be invoked.
 If they were errors they will be printed to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.

 @return @c The result of calling the method. Value of the @c double type.
 */
_AMICOPYJAVA_API double callDoubleMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return 0;

    std::string signature = getMethodSignature(midUInt);
    if (signature.empty())
        return 0;

    va_list vl;
    va_start(vl, midUInt);
    double res = callTMethod<double>(midUInt, signature, vl);
    va_end(vl);

    return res;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Programmers place all arguments that are to be passed to the method immediately
 following the @c signature argument. The @c callJObjectMethod function
 accepts these arguments, converts and passes them to the method that the programmer
 would like to be invoked.
 If they were errors they will be printed to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param signature Signature of the method.
 @param vl Additional arguments to be passed in to the invokable method of the @c Java class.

 @return @c The result of calling the method. Value of the @c jobject type.
 */
_AMICOPYJAVA_API jobject callJObjectMethod(void* midUInt, std::string signature, va_list vl)
{
    if (!check_midUInt(midUInt))
        return NULL;
    
    jobject res = callTMethod<jobject>(midUInt, signature, vl);

    return res;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Programmers place all arguments that are to be passed to the method immediately
 following the @c signature argument. The @c callJObjectMethodL function
 accepts these arguments, converts and passes them to the method that the programmer
 would like to be invoked.
 If they were errors they will be printed to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param signature Signature of the method.
 @param ... Additional arguments to be passed in to the invokable method of the @c Java class.

 @return @c The result of calling the method. Value of the @c jobject type.
 */
_AMICOPYJAVA_API jobject callJObjectMethodL(void* midUInt, std::string signature, ...)
{
    if (!check_midUInt(midUInt))
        return NULL;

    va_list vl;
    va_start(vl, signature);
    jobject res = callTMethod<jobject>(midUInt, signature, vl);
    va_end(vl);

    return res;
}

//******************************************************************
//Arrays

/**
 @brief Sets size of passed to the AmiCo function array(s).

 If they were errors they would be emitted to the standard error output.
 
 @param size Number of elements in array, that will be passed to this method.  In other words @c size should be equal to the number of arrays that will be passed to the following function.
 @param arrays Array of length @c size of integers where each element means size of appropriate array in the following function.
 
 @attention This function should be invoked before calling of the method that takes arrays as arguments.
 */
_AMICOPYJAVA_API void setSizeOfArrays(unsigned int size, unsigned int* arrays)
{
    if (size < 0)
    {
        std::cerr << "AmiCo: setSizeOfArrays: value of size is negative!";
        std::cerr.flush();
        return;
    }

    if (arraysSize != NULL)
        delete[] arraysSize;

    arraysSize = new unsigned int[size];
    unsigned int* tmp = new unsigned int[size];
    std::copy(arrays, arrays + size, arraysSize);
}

/** 
 @brief Generalizes call of the @c convertJavaArrayToPythonArray function and @c JNI @c CallObjectMethod function.
 
 This function calls @c callJObjectMethod with corresponding arguments and takes a @c Java array as result.
 Then this array converted to @c Python tuple using convertJavaArrayToPythonArray function.
 
 @param midUInt ID of the method.
 @param vl Additional arguments to be passed in to the @c Java method.
 
 @return @c PyObject*.
 */
template<typename TypeArray, typename Type>
PyObject* callTypeArrayMethod(void* midUInt, va_list vl)
{
    if (!check_midUInt(midUInt))
        return Py_None;
    
    std::string signature = getMethodSignature(midUInt);
    if (signature.empty())
        return Py_None;

    mthdSign sign = parseSignature(signature);
    
    TypeArray array = (TypeArray) callJObjectMethod(midUInt, signature, vl);

    if (array == NULL)
    {
        std::cerr << "AmiCo: WARNING: call<Type>ArrayMethod: array is NULL. Nothing to convert. Return Py_None.";
        return Py_None;
    }

    sign.ret.pop();
    return convertJavaArrayToPythonArray<TypeArray, Type>(env, array, sign.ret.front());
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Invokes an instance method, specified using a method ID. Programmers place all arguments
 that are to be passed to the method immediately following the @c midUInt argument.
 The @c callIntArrayMethod function accepts these arguments, converts and
 passes them to the method that the programmer would like to be invoked.
 If they were errors they would be emitted to the standard error output.
 
 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.
 
 @return The result of calling the method --- @c Python tuple.
 */
_AMICOPYJAVA_API PyObject* callIntArrayMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return Py_None;

    va_list vl;
    va_start(vl, midUInt);
    PyObject* o = callTypeArrayMethod<jintArray, jint>(midUInt, vl);
    va_end(vl);
    return o;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Invokes an instance method, specified using a method ID. Programmers place all arguments
 that are to be passed to the method immediately following the @c midUInt argument.
 The @c callLongArrayMethod function accepts these arguments, converts and
 passes them to the method that the programmer would like to be invoked.
 If they were errors they would be emitted to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.

 @return The result of calling the method --- @c Python tuple.
 */
_AMICOPYJAVA_API PyObject* callLongArrayMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return Py_None;

    va_list vl;
    va_start(vl, midUInt);
    PyObject* o = callTypeArrayMethod<jlongArray, jlong>(midUInt, vl);
    va_end(vl);
    return o;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Invokes an instance method, specified using a method ID. Programmers place all arguments
 that are to be passed to the method immediately following the @c midUInt argument.
 The @c callShortArrayMethod function accepts these arguments, converts and
 passes them to the method that the programmer would like to be invoked.
 If they were errors they would be emitted to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.

 @return The result of calling the method --- @c Python tuple.
 */
_AMICOPYJAVA_API PyObject* callShortArrayMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return Py_None;

    va_list vl;
    va_start(vl, midUInt);
    PyObject* o = callTypeArrayMethod<jshortArray, jshort>(midUInt, vl);
    va_end(vl);
    return o;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Invokes an instance method, specified using a method ID. Programmers place all arguments
 that are to be passed to the method immediately following the @c midUInt argument.
 The @c callByteArrayMethod function accepts these arguments, converts and
 passes them to the method that the programmer would like to be invoked.
 If they were errors they would be emitted to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.

 @return The result of calling the method --- @c Python tuple.
 */
_AMICOPYJAVA_API PyObject* callByteArrayMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return Py_None;

    va_list vl;
    va_start(vl, midUInt);
    PyObject* o = callTypeArrayMethod<jbyteArray, jbyte>(midUInt, vl);
    va_end(vl);
    return o;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Invokes an instance method, specified using a method ID. Programmers place all arguments
 that are to be passed to the method immediately following the @c midUInt argument.
 The @c callBooleanArrayMethod function accepts these arguments, converts and
 passes them to the method that the programmer would like to be invoked.
 If they were errors they would be emitted to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.

 @return The result of calling the method --- @c Python tuple.
 */
_AMICOPYJAVA_API PyObject* callBooleanArrayMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return Py_None;

    va_list vl;
    va_start(vl, midUInt);
    PyObject* o = callTypeArrayMethod<jbooleanArray, jboolean>(midUInt, vl);
    va_end(vl);
    return o;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Invokes an instance method, specified using a method ID. Programmers place all arguments
 that are to be passed to the method immediately following the @c midUInt argument.
 The @c callCharArrayMethod function accepts these arguments, converts and
 passes them to the method that the programmer would like to be invoked.
 If they were errors they would be emitted to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.

 @return The result of calling the method --- @c Python tuple.
 */
_AMICOPYJAVA_API PyObject* callCharArrayMethod(void* midUInt, ...)
{
    va_list vl;
    va_start(vl, midUInt);
    PyObject* o = callTypeArrayMethod<jcharArray, jchar>(midUInt, vl);
    va_end(vl);
    return o;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Invokes an instance method, specified using a method ID. Programmers place all arguments
 that are to be passed to the method immediately following the @c midUInt argument.
 The @c callFloatArrayMethod function accepts these arguments, converts and
 passes them to the method that the programmer would like to be invoked.
 If they were errors they would be emitted to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.

 @return The result of calling the method --- @c Python tuple.
 */
_AMICOPYJAVA_API PyObject* callFloatArrayMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return Py_None;

    va_list vl;
    va_start(vl, midUInt);
    PyObject* o = callTypeArrayMethod<jfloatArray, jfloat>(midUInt, vl);
    va_end(vl);
    return o;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Invokes an instance method, specified using a method ID. Programmers place all arguments
 that are to be passed to the method immediately following the @c midUInt argument.
 The @c callDoubleArrayMethod function accepts these arguments, converts and
 passes them to the method that the programmer would like to be invoked.
 If they were errors they would be emitted to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.

 @return The result of calling the method --- @c Python tuple.
 */
_AMICOPYJAVA_API PyObject* callDoubleArrayMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return Py_None;

    va_list vl;
    va_start(vl, midUInt);
    PyObject* o = callTypeArrayMethod<jdoubleArray, jdouble>(midUInt, vl);
    va_end(vl);
    return o;
}

/**
 @brief Invokes an instance method, specified using a method ID.

 Invokes an instance method, specified using a method ID. Programmers place all arguments
 that are to be passed to the method immediately following the @c midUInt argument.
 The @c callStringArrayMethod function accepts these arguments, converts and
 passes them to the method that the programmer would like to be invoked.
 If they were errors they would be emitted to the standard error output.

 @param midUInt An ID of the method, returned by @c getMethod function.
 @param ... Arguments to be passed in to the invokable method of the @c Java class.

 @return The result of calling the method --- @c Python tuple.
 */
_AMICOPYJAVA_API PyObject* callStringArrayMethod(void* midUInt, ...)
{
    if (!check_midUInt(midUInt))
        return Py_None;

    va_list vl;
    va_start(vl, midUInt);
    PyObject* o = callTypeArrayMethod<jobjectArray, jobject>(midUInt, vl);
    va_end(vl);
    return o;
}

//******************************************************************

/**
 @brief Unloads @c Java class from the memory and destroys @c JVM.

 If they were errors they will be printed to the standard error output.
 */
_AMICOPYJAVA_API void destroyEnvironment()
{
    //std::cout <<std::endl << "Destroying JVM!" << std::endl;
    if (arraysSize != NULL)
        delete[] arraysSize;

    (jvm)->DestroyJavaVM();
    exit(0);
}

/** 
 @brief Destroys @c Java @c Virtual @c Machine.
 
 This function used when internal error occured and JVM should be destroyed.
 Return code is -1.
 */
void destroyVM() {
    if (arraysSize != NULL)
        delete[] arraysSize;
    
    std::cerr << "\n\nAmiCo: Internal ERROR! DESTROYING VM\n\n";
    std::cerr.flush();
    if ((env)->ExceptionOccurred()) {
        (env)->ExceptionDescribe();
    }
    (jvm)->DestroyJavaVM();
    exit(-1);
}

/*
 * Insert your code below
 */

//void createEnvironmentApple(const char* javaClassName, int nOptions = 0, const char* argv = NULL);

//#ifdef __APPLE__

/*Starts a JVM using the options,classpath,main class, and args stored in a VMLauchOptions structure */
/*
static void* startupJava(void *options) {
    int result = 0;
    //    JNIEnv* env;
    //    JavaVM* theVM;

    VMLaunchOptions * launchOptions = (VMLaunchOptions*) options;*/

    /* default vm args */
    //JavaVMInitArgs vm_args;

    /*
    To invoke Java 1.4.1 or the currently preferred JDK as defined by the operating system (1.4.2 as of the release of this sample and the release of Mac OS X 10.4) nothing changes in 10.4 vs 10.3 in that when a JNI_VERSION_1_4 is passed into JNI_CreateJavaVM as the vm_args.version it returns the current preferred JDK.

    To specify the current preferred JDK in a family of JVM's, say the 1.5.x family, applications should set the environment variable JAVA_JVM_VERSION to 1.5, and then pass JNI_VERSION_1_4 into JNI_CreateJavaVM as the vm_args.version. To get a specific Java 1.5 JVM, say Java 1.5.0, set the environment variable JAVA_JVM_VERSION to 1.5.0. For Java 1.6 it will be the same in that applications will need to set the environment variable JAVA_JVM_VERSION to 1.6 to specify the current preferred 1.6 Java VM, and to get a specific Java 1.6 JVM, say Java 1.6.1, set the environment variable JAVA_JVM_VERSION to 1.6.1.

    To make this sample bring up the current preferred 1.5 JVM, set the environment variable JAVA_JVM_VERSION to 1.5 before calling JNI_CreateJavaVM as shown below.  Applications must currently check for availability of JDK 1.5 before requesting it.  If your application requires JDK 1.5 and it is not found, it is your responsibility to report an error to the user. To verify if a JVM is installed, check to see if the symlink, or directory exists for the JVM in /System/Library/Frameworks/JavaVM.framework/Versions/ before setting the environment variable JAVA_JVM_VERSION.

    If the environment variable JAVA_JVM_VERSION is not set, and JNI_VERSION_1_4 is passed into JNI_CreateJavaVM as the vm_args.version, JNI_CreateJavaVM will return the current preferred JDK. Java 1.4.2 is the preferred JDK as of the release of this sample and the release of Mac OS X 10.4.
     */
   /* {
        CFStringRef targetJVM = CFSTR("1.5");
        CFBundleRef JavaVMBundle;
        CFURLRef JavaVMBundleURL;
        CFURLRef JavaVMBundlerVersionsDirURL;
        CFURLRef TargetJavaVM;
        UInt8 pathToTargetJVM [PATH_MAX] = "\0";
        struct stat sbuf;


        // Look for the JavaVM bundle using its identifier
        JavaVMBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.JavaVM"));

        if (JavaVMBundle != NULL) {
            // Get a path for the JavaVM bundle
            JavaVMBundleURL = CFBundleCopyBundleURL(JavaVMBundle);
            CFRelease(JavaVMBundle);

            if (JavaVMBundleURL != NULL) {
                // Append to the path the Versions Component
                JavaVMBundlerVersionsDirURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, JavaVMBundleURL, CFSTR("Versions"), true);
                CFRelease(JavaVMBundleURL);

                if (JavaVMBundlerVersionsDirURL != NULL) {
                    // Append to the path the target JVM's Version
                    TargetJavaVM = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, JavaVMBundlerVersionsDirURL, targetJVM, true);
                    CFRelease(JavaVMBundlerVersionsDirURL);

                    if (TargetJavaVM != NULL) {
                        if (CFURLGetFileSystemRepresentation(TargetJavaVM, true, pathToTargetJVM, PATH_MAX)) {
                            // Check to see if the directory, or a sym link for the target JVM directory exists, and if so set the
                            // environment variable JAVA_JVM_VERSION to the target JVM.
                            if (stat((char*) pathToTargetJVM, &sbuf) == 0) {
                                // Ok, the directory exists, so now we need to set the environment var JAVA_JVM_VERSION to the CFSTR targetJVM
                                // We can reuse the pathToTargetJVM buffer to set the environement var.
                                if (CFStringGetCString(targetJVM, (char*) pathToTargetJVM, PATH_MAX, kCFStringEncodingUTF8))
                                    setenv("JAVA_JVM_VERSION", (char*) pathToTargetJVM, 1);
                            }
                        }
                        CFRelease(TargetJavaVM);
                    }
                }
            }
        }
    }*/

    /* JNI_VERSION_1_4 is used on Mac OS X to indicate the 1.4.x and later JVM's */
    /*vm_args.version = JNI_VERSION_1_4;
    vm_args.options = (JavaVMOption*) launchOptions->options;
    vm_args.nOptions = launchOptions->nOptions;
    vm_args.ignoreUnrecognized = JNI_TRUE;*/

    /* start a VM session */
    /*result = JNI_CreateJavaVM(&jvm, (void**) & env, &vm_args);

    if (result != 0) {
        fprintf(stderr, "[JavaAppLauncher Error] Error starting up VM.\n");
        exit(result);
        return NULL;
    }*/

    /* Find the main class */
   /* jclass mainClass = (env)->FindClass(launchOptions->mainClass);
    if (mainClass == NULL) {
        (env)->ExceptionDescribe();
        result = -1;
        goto leave;
    }
    jClass = mainClass;
    std::cout << "!!cls = " << jClass;
    std::cout.flush();*/

    /* Get the application's main method */
     /*jmethodID mainID = (env)->GetStaticMethodID(mainClass, "main",
            "([Ljava/lang/String;)V");
    if (mainID == NULL) {
        if ((env)->ExceptionOccurred()) {
            (env)->ExceptionDescribe();
        } else {
            fprintf(stderr, "[JavaAppLauncher Error] No main method found in specified class.\n");
        }
        result = -1;
        goto leave;
    }*/

    /* Build argument array */
   /* jobjectArray mainArgs = NewPlatformStringArray(env, (char **) launchOptions->args, launchOptions->numberOfArgs);
    if (mainArgs == nil) {
        (env)->ExceptionDescribe();
        goto leave;
    }*/

    /* or create an empty array of java.lang.Strings to pass in as arguments to the main method
    jobjectArray mainArgs = (*env)->NewObjectArray(env, 0,
                        (*env)->FindClass(env, "java/lang/String"), NULL);
    if (mainArgs == 0) {
        result = -1;
        goto leave;
    }
     */

    /* Invoke main method passing in the argument object. */
    /*(env)->CallStaticVoidMethod(mainClass, mainID, mainArgs);
    if ((env)->ExceptionOccurred()) {
        (env)->ExceptionDescribe();
        result = -1;
        goto leave;
    }
    std::cout << "HERE1?";
    std::cout.flush();
    CFRunLoopStop(CFRunLoopGetCurrent());
    std::cout << "HERE2?";
    return env;
leave:
    freeVMLaunchOptions(launchOptions);
    (jvm)->DestroyJavaVM();
    exit(result);
    return NULL;
}*/

/* call back for dummy source used to make sure the CFRunLoop doesn't exit right away */

/* This callback is called when the source has fired. */
/*void sourceCallBack(void *info)
{
    std::cout << "\nC++: OUT!";
    std::cout.flush();
}*/

/*  The following code will spin a new thread off to start the JVM
    while using the primordial thread to run the main runloop.
 */

/*
int mainFun(int argc, const char **argv) {
    CFRunLoopSourceContext sourceContext;

    for (int i = 0; i < argc; ++i)
        printf("argv = %s\n", argv[i]);*/

    /* Start the thread that runs the VM. */
    //pthread_t vmthread;

    /* Parse the args */
    //VMLaunchOptions * launchOptions = NewVMLaunchOptions(argc, argv);

    //    launchOptions->mainClass = "HelloWorld";

    /* Set our name for the Application Menu to our Main class */
    //setAppName(launchOptions->mainClass);

    /* create a new pthread copying the stack size of the primordial pthread */
    /*struct rlimit limit;
    size_t stack_size = 0;
    int rc = getrlimit(RLIMIT_STACK, &limit);
    if (rc == 0) {
        if (limit.rlim_cur != 0LL) {
            stack_size = (size_t) limit.rlim_cur;
        }
    }

    pthread_attr_t thread_attr;
    pthread_attr_init(&thread_attr);
    pthread_attr_setscope(&thread_attr, PTHREAD_SCOPE_SYSTEM);
    pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
    if (stack_size > 0) {
        pthread_attr_setstacksize(&thread_attr, stack_size);
    }*/

    /* Start the thread that we will start the JVM on. */
    /*std::cout << "\nTrying to create a VM...";
    pthread_create(&vmthread, &thread_attr, startupJava, launchOptions);
    pthread_attr_destroy(&thread_attr);

    std::cout << "\nCreated VM!...";*/
    /* Create a a sourceContext to be used by our source that makes */
    /* sure the CFRunLoop doesn't exit right away */
   /* sourceContext.version = 0;
    sourceContext.info = NULL;
    sourceContext.retain = NULL;
    sourceContext.release = NULL;
    sourceContext.copyDescription = NULL;
    sourceContext.equal = NULL;
    sourceContext.hash = NULL;
    sourceContext.schedule = NULL;
    sourceContext.cancel = NULL;
    sourceContext.perform = &sourceCallBack;*/

    /* Create the Source from the sourceContext */
   // CFRunLoopSourceRef sourceRef = CFRunLoopSourceCreate(NULL, 0, &sourceContext);
    //    Ref = sourceRef;

    /* Use the constant kCFRunLoopCommonModes to add the source to the set of objects */
    /* monitored by all the common modes */
    //CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes);

    /* Park this thread in the runloop */
   // std::cout << "\nBefore !...";
   // CFRunLoopRun();

  //  std::cout << "\nRETURN !...";
   // return 0;
//}
//#endif

/*void createEnvironmentApple(const char* javaClassName, int nOptions = 0, const char* argv = NULL)
{
#ifdef __APPLE__
    int argc = 2;
    const char** argm = new const char*[2];
    argm[0] = "./dummy";
    argm[1] = javaClassName;
    printf("argc = %d\n", argc);
    std::cout << "\nCall to main!";
    mainFun(2, argm);
    std::cout << "\nRETUUUURN!";
#endif
}*/